package org.esky.mail;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

import org.apache.log4j.Logger;

public class SimpleMailSender {
	
	private static Logger logger = Logger.getLogger(SimpleMailSender.class);
	private MailSenderInfo mailInfo = null;
	private Session sendMailSession = null;
	private Message mailMessage = null;
	// 用时
	private long usedTime = 0;
	// 邮件编码
	private static final String CHARSET = "GBK";
	// 调试模式
	private static final boolean isDebug = false;
	
	public SimpleMailSender(MailSenderInfo mailInfo) {
		this.mailInfo = mailInfo;
	}
	
	/**
	 * 创建Session对象，此时需要配置传输的协议，是否身份认证
	 * @throws Exception 
	 */
	private void createSession() throws Exception {
		logger.info("Mail server info：");
		logger.info("-------------------------------------");
		this.usedTime = System.currentTimeMillis();
		if (mailInfo == null) {
			logger.error("MailSenderInfo is null");
			throw new Exception();
		}
		// 判断是否需要身份认证
		UserAuthenticator authenticator = null;
		Properties properties = mailInfo.getProperties();
		if (properties == null) {
			logger.error("Mail server properties is null!");
			throw new Exception();
		}
		logger.info("Transport protocol：" + properties.get("mail.transport.protocol"));
		logger.info("Host：" + properties.get("mail.smtp.host"));
		logger.info("Porh：" + properties.get("mail.smtp.port"));
		logger.info("Auth：" + properties.get("mail.smtp.auth"));
		if (mailInfo.isValidate()) {
			// 如果需要身份认证，则创建一个密码验证器
			logger.info("UserName：" + mailInfo.getUserName());
			logger.info("Password：" + mailInfo.getPassword());
			authenticator = new UserAuthenticator(mailInfo.getUserName(), mailInfo.getPassword());
		}
		// 根据邮件会话属性和密码验证器构造一个发送邮件的session 
		Session sendMailSession = Session.getDefaultInstance(properties, authenticator);
		sendMailSession.setDebug(isDebug);
		this.sendMailSession = sendMailSession; 
	}
	
	/**
	 * 创建Message对象
	 */
	private void createMessage() throws Exception{
		System.out.println();
		logger.info("Mail message info：");
		logger.info("-------------------------------------");
		if (sendMailSession == null) {
			logger.error("Session is null");
			throw new Exception();
		}
		Message mailMessage = new MimeMessage(sendMailSession);
		// 创建邮件发送者地址
		logger.info("FromAddress：" + mailInfo.getFromAddress());
		Address from = new InternetAddress(mailInfo.getFromAddress());
		// 设置邮件消息的发送者
		mailMessage.setFrom(from);
		// 创建邮件的接收者地址，并设置到邮件消息中   
		Address[] tos = null;
		String[] receivers = mailInfo.getReceivers();
		if (receivers != null) {// 当有多个接收者时
			// 为每个邮件接收者创建一个地址
			tos = new InternetAddress[receivers.length + 1];
			logger.info("ToAddress：" + mailInfo.getToAddress());
			tos[0] = new InternetAddress(mailInfo.getToAddress());
			for (int i = 0; i < receivers.length; i++) {
				logger.info("Receiver：" + receivers[i]);
				tos[i + 1] = new InternetAddress(receivers[i]);
			}
		} else {// 只有一个接收者
			tos = new InternetAddress[1];
			logger.info("ToAddress：" + mailInfo.getToAddress());
			tos[0] = new InternetAddress(mailInfo.getToAddress());
		}
		// 将所有接收者地址都添加到邮件接收者属性中
		mailMessage.addRecipients(Message.RecipientType.TO, tos);
		// 获取抄送者信息
		String[] ccs = mailInfo.getCcs();
		if (ccs != null) {
			// 为每个邮件接收者创建一个地址
			Address[] ccAdresses = new InternetAddress[ccs.length];
			for (int i = 0; i < ccs.length; i++) {
				logger.info("Cc：" + ccs[i]);
				ccAdresses[i] = new InternetAddress(ccs[i]);
			}
			// 将抄送者信息设置到邮件信息中，注意类型为Message.RecipientType.CC
			mailMessage.setRecipients(Message.RecipientType.CC, ccAdresses);
		}
		// 获取密送者信息
		String[] bccs = mailInfo.getBccs();
		if (bccs != null) {
			// 为每个邮件接收者创建一个地址
			Address[] bccAdresses = new InternetAddress[bccs.length];
			for (int i = 0; i < bccs.length; i++) {
				logger.info("Bcc：" + bccs[i]);
				bccAdresses[i] = new InternetAddress(bccs[i]);
			}
			// 将密送者信息设置到邮件信息中，注意类型为Message.RecipientType.BCC
			mailMessage.addRecipients(Message.RecipientType.BCC, bccAdresses);
		}
		// 设置邮件消息的主题   
		logger.info("Subject：" + mailInfo.getSubject());
		mailMessage.setSubject(mailInfo.getSubject());
		// 设置邮件消息发送的时间
		logger.info("SentDate：" + new Date());
		mailMessage.setSentDate(new Date());
		logger.info("ContentType：" + mailInfo.getContentType());
		// MiniMultipart类是一个容器类，包含MimeBodyPart类型的对象
		Multipart mainPart = null;
		// 以文本格式发送邮件
		if (mailInfo.getContentType().equals(MailSenderInfo.ContentType.TEXT)) {
			// 设置邮件消息的主要内容
			logger.info("Content：" + mailInfo.getContent());
			String mailContent = mailInfo.getContent();
			mailMessage.setText(mailContent);
		}
		// 以HTML格式发送邮件
		else if (mailInfo.getContentType().equals(MailSenderInfo.ContentType.HTML)) {
			// 用于组合文本和图片，"related"型的MimeMultipart对象
			mainPart = new MimeMultipart("related");
			// 创建一个包含HTML内容的MimeBodyPart
			BodyPart body = new MimeBodyPart();
			// 设置HTML内容
			logger.info("Content：" + mailInfo.getContent());
			body.setContent("<meta http-equiv=Content-Type content=text/html; charset="+CHARSET+">" + mailInfo.getContent(), "text/html; charset="+CHARSET);
			mainPart.addBodyPart(body);
		} else {
			logger.error("mail's contents is null!");
			throw new Exception();
		}
		// 为邮件添加附件
		mainPart = createAttachFile(mainPart);
		if (mainPart != null) {
			// 将MiniMultipart对象设置为邮件内容
			mailMessage.setContent(mainPart);
		}
		this.mailMessage = mailMessage;
		
	}
	
	/**
	 * 创建邮件附件对象
	 */
	private Multipart createAttachFile(Multipart mainPart) throws MessagingException, UnsupportedEncodingException{
		// 为邮件添加附件
		if (mailInfo.getAttachFileNames() != null && mailInfo.getAttachFileNames().length > 0) {
			// 存放邮件附件的MimeBodyPart
			MimeBodyPart attachment = null;
			File file = null;
			String[] attachFileNames = mailInfo.getAttachFileNames();
			if (mainPart == null) {
				mainPart = new MimeMultipart();
			}
			for (int i = 0; i < attachFileNames.length; i++) {
				attachment = new MimeBodyPart();
				// 根据附件文件创建文件数据源
				file = new File(attachFileNames[i]);
				FileDataSource fds = new FileDataSource(file);
				attachment.setDataHandler(new DataHandler(fds));
				logger.info("AttachFileName：" + file.getName());
				// 为附件设置文件名
				attachment.setFileName(encodeWord(file.getName()));
				if (isPic(file.getName())) {
					//cid [图片]命名规则 名字_类型
					String cid = encodeWord(file.getName().replace(".", "_"));
					attachment.setContentID(cid);
				}
				mainPart.addBodyPart(attachment);
			}
		}
		return mainPart;
	}
	
	/**
	 * 转码 处理邮件中中文不能识别的问题
	 */
	private String encodeWord(String word) throws UnsupportedEncodingException{
		return MimeUtility.encodeWord(word, CHARSET, null);
	}
	
	
	/**
	 * 判断该附件是否内是容图片
	 */
	private boolean isPic(String fileName) throws UnsupportedEncodingException{
		String content = mailInfo.getContent().replaceAll("\\s+", "");
		content = content.replaceAll("\\s+", "");
		String img;
		for (int i = 0; i < content.length(); i++) {
			if (content.indexOf("<img") >= 0) {
				String tmp = content.substring(content.indexOf("<img"));
				img = tmp.substring(0, tmp.indexOf(">")+1);
				content = tmp.substring(tmp.indexOf(">")+1, tmp.length());
				if (img.indexOf("src=\"cid:") >= 0 && img.indexOf(encodeWord(fileName.replace(".", "_"))) >= 0) {
					return true;
				}
			}
		}
		return false;
	}
	
	public boolean send(){
		try {
			// 创建
			createSession();
			createMessage();
			Transport transport = sendMailSession.getTransport();
			// 发送
			Transport.send(mailMessage);
			this.usedTime = System.currentTimeMillis() - this.usedTime;
			logger.info("Mail send success in "+this.usedTime+" ms");
			// 关闭
			transport.close(); 
			return true;
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (AddressException e) {
			e.printStackTrace();
		} catch (MessagingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.error("Mail send faild");
		return false;
	}
	
}
